/*
 * Copyright (C) 2011-2018 ShenZhen iBOXSaaS Information Technology Co.,Ltd.
 * 
 * All right reserved.
 * 
 * This software is the confidential and proprietary information of iBOXSaaS Company of China.
 * ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only in
 * accordance with the terms of the contract agreement you entered into with iBOXSaaS inc.
 * 
 */

package com.iboxpay.open.gateway.service.impl;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.iboxpay.open.common.constants.ParamEnums;
import org.iboxpay.open.common.constants.ResultEnums;
import org.iboxpay.open.common.exception.BusinessException;
import org.iboxpay.open.common.token.RsaUtil;
import org.iboxpay.open.common.token.VerifyResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import com.iboxpay.open.common.db.dao.AppUserInfoDao;
import com.iboxpay.open.common.db.dao.AuthTokenRecordDao;
import com.iboxpay.open.common.db.entity.AppUserInfo;
import com.iboxpay.open.common.db.entity.AuthTokenRecord;
import com.iboxpay.open.gateway.basedata.BaseData;
import com.iboxpay.open.gateway.config.TokenConfig;
import com.iboxpay.open.gateway.service.TokenValidateService;
import com.iboxpay.open.gateway.utils.ParamUtils;
import com.iboxpay.open.gateway.vo.ResScopeVo;

import io.jsonwebtoken.Claims;

/**
 * 
 * TokenValidateServiceImpl.java
 * 
 * @author xuxiao
 * @date 2018/06/22
 */
@Service
public class TokenValidateServiceImpl implements TokenValidateService {
    
    private static final String TOKEN_NAME = "OPEN_ACCESS_TOKEN";
    
    private PathMatcher pathMatcher = new AntPathMatcher();
    
    @Autowired
    private BaseData baseData;
    
    @Autowired
    private TokenConfig tokenConfig;
    
    @Autowired
    private AuthTokenRecordDao authTokenRecordDao;
    
    @Autowired
    private AppUserInfoDao appUserInfoDao;

    @Override
    public Claims tokenValidate(HttpServletRequest request) {
        String requestUri = request.getRequestURI();
        // 1、获取uri，判断是否需要token验证
        ResScopeVo resScope = resMatch(requestUri);
        if (resScope == null || !resScope.isVerifyToken()) {
            // doNothing
            return null;
        }
        // 2、验证token
        String accessToken = StringUtils.isBlank(request.getHeader(TOKEN_NAME)) ? request.getParameter(TOKEN_NAME)
            : request.getHeader(TOKEN_NAME);
        if (StringUtils.isBlank(accessToken)) {
            throw new BusinessException(ResultEnums.tokenMiss);
        }
        VerifyResult<Claims> verifyResult = RsaUtil.verify(accessToken, tokenConfig.getPublicKeyStr());
        if (!verifyResult.isVaild()) {
            throw new BusinessException(verifyResult.getResultCode());
        }
        Claims claims = verifyResult.getResult();
        // 3、验证token类型
        String tokenType = claims.get(ParamEnums.tokenType.getCode(), String.class);
        if(!ParamEnums.accessToken.getCode().equals(tokenType)) {
            throw new BusinessException(ResultEnums.refreshTokenCanNotAccRes);
        }
        // 4、验证token作用域
        String scope = claims.get(ParamEnums.scope.getCode(), String.class);
        if (StringUtils.isEmpty(scope) || resScope.getScopeCode() == null) {
            throw new BusinessException(ResultEnums.scopeCheckFail);
        }
        String[] scopes = scope.split(",");
        Set<String> scopeSet = new HashSet<>(Arrays.asList(scopes));
        scopeSet.retainAll(resScope.getScopeCode());
        if (scopeSet.isEmpty()) {
            throw new BusinessException(ResultEnums.scopeCheckFail);
        }
        return claims;
    }
    
    /**
     * 根据请求地址匹配资源
     * 
     * @param requestUri 请求地址
     * @return 资源vo
     */
    private ResScopeVo resMatch(String requestUri) {
        for (Entry<String, ResScopeVo> entry : baseData.resTokenCheckMap.entrySet()) {
            if (this.pathMatcher.match(entry.getKey(), requestUri)) {
                return entry.getValue();
            }
        }
        return null;
    }
    
    
    @Override
    public String getUserId(Long openId) {
        if(openId == 0) {
            return "";
        }
        AppUserInfo appUserInfo = appUserInfoDao.selectByPrimaryKey(openId);
        return appUserInfo == null ? "" : appUserInfo.getUserId();
    }

}
